﻿using HS.Log.Models;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web;

namespace HS.Log.Util
{
    public class BodyFactory
    {
        /// <summary>
        /// 获取日志体
        /// </summary>
        /// <param name="msg"></param>
        /// <param name="source"></param>
        /// <param name="tranCode"></param>
        /// <param name="mark"></param>
        /// <param name="stack"></param>
        /// <returns></returns>
        internal static LogBody GetLogEntity(object msg, string source, string tranCode, string mark, StackFrame stack = null)
        {
            var traceID = string.Empty;
            var result = TryGetTraceID(out traceID);
            if (!result)
            {
                var tEntity = GetDefaultEntity(msg, source, tranCode, mark);
                return GetEnitity(msg, source, tranCode, mark, tEntity, stack);
            }
            LogBody logBody = HttpContext.Current.Items[traceID] as LogBody;

            if (logBody == null)
            {
                logBody = GetDefaultEntity(msg, source, tranCode, mark);
                GetEnitity(msg, source, tranCode, mark, logBody, stack);
            }
            logBody.CreateTime = DateTime.Now.ToUniversalTime();
            logBody.RpcId++;
            return GetEnitity(msg, source, tranCode, mark, logBody, stack);
        }

        private static bool TryGetTraceID(out string traceId)
        {
            try
            {
                traceId = HttpContext.Current.Response.Headers["traceID"];

                return !string.IsNullOrEmpty(traceId);
            }
            catch
            {
                traceId = string.Empty;
                return false;
            }
        }

        private static LogEntity GetEnitity(object msg, string source, string tranCode, string mark, LogBody logbody, StackFrame stack = null)
        {
            var traceID = string.Empty;
            LogEntity entity = new LogEntity();
            entity.TraceId = logbody.TraceId;
            entity.RpcId = logbody.RpcId;
            entity.CreateTime = DateTime.Now.ToUniversalTime();
            entity.Source = source;
            entity.TranCode = tranCode;
            entity.Mark = mark;
            entity.Message = msg is string ? msg as string : JsonConvert.SerializeObject(msg);
            entity.FilePath = string.Format("{0}/{1}/{2}log.log", DateTime.Now.ToString("yyyyMMdd"), tranCode, mark);
            TryGetStackInfo(stack, entity);
            return entity;
        }

        private static void TryGetStackInfo(StackFrame stack, LogEntity entity)
        {
            if (stack != null)
            {
                try
                {
                    var method = stack.GetMethod();
                    entity.ClassName = method.DeclaringType.Name;
                    entity.Namespace = method.DeclaringType.Namespace;
                    entity.Module = method.Module.ToString();
                    entity.MethodName = method.Name;
                    entity.LineNumber = stack.GetFileLineNumber();
                    entity.StackInfo = string.Format("\t模块:{0}\n\t命名空间:{1}\n\t类:{2}\n\t方法:{3}\n\t行:{4}\n", method.Module
                        , method.DeclaringType.Namespace,
                        method.DeclaringType.Name,
                        method.Name, stack.GetFileLineNumber());
                }
                catch { }
            }
        }

        private static LogBody GetDefaultEntity(object msg, string source, string tranCode, string mark)
        {
            LogEntity logBody = new LogEntity();
            logBody.TraceId = Guid.NewGuid().ToString("N");
            logBody.RpcId = 1;
            logBody.CreateTime = DateTime.Now.ToUniversalTime();
            logBody.Message = msg is string ? msg as string : JsonConvert.SerializeObject(msg);
            logBody.Source = source;
            logBody.Mark = mark;
            logBody.TranCode = tranCode;
            TrySetTrace(logBody);
            return logBody;
        }

        private static void TrySetTrace(LogEntity logBody)
        {
            try
            {
                HttpContext.Current.Response.AppendHeader("traceID", logBody.TraceId);
                HttpContext.Current.Items.Add(logBody.TraceId, logBody);
            }
            catch { }
        }
    }
}
